home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 September / Macworld (1998-09).dmg / Shareware World / Info / For Developers / MacZoop 1.8.3 / Required Classes / Z Sources / ZArray.cpp < prev    next >
Text File  |  1998-07-10  |  19KB  |  716 lines

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            MacZoop - "the framework for the rest of us"         
  5. *
  6. *
  7. *
  8. *            ZArray.cpp            -- the basic container class object
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            © 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21.  
  22. #include    "ZArray.h"
  23. #include    "MacZoop.h"
  24.  
  25.  
  26. static short    vCompareFunc( void* a, void* b, const long ref);
  27.  
  28.  
  29. CLASSCONSTRUCTOR( ZArray );
  30.  
  31. /*-------------------------------***  CONSTRUCTOR  ***----------------------------------*/
  32.  
  33.  
  34. ZArray::ZArray( short elementSize )
  35.     : ZComrade()
  36. {
  37.     classID = CLASS_ZArray;
  38.     
  39.     blkSize = elementSize;
  40.     numElements = 0;
  41.     physicalBlks = kNumPhysicalBlockAlloc;
  42.     
  43.     FailNIL( a = NewHandle( elementSize * physicalBlks ));
  44. }
  45.  
  46.  
  47. /*--------------------------------***  DESTRUCTOR  ***----------------------------------*/
  48.  
  49. ZArray::~ZArray()
  50. {
  51.     if (a)
  52.         DisposeHandle( a );
  53. }
  54.  
  55.  
  56. /*--------------------------------***  INSERTITEM  ***----------------------------------*/
  57. /*    
  58. Insert an item at position <index>
  59. ----------------------------------------------------------------------------------------*/
  60.  
  61. void    ZArray::InsertItem(void* item, const long index)
  62. {
  63.     // insert the item into the array at position <index>. The value of <index> is one-based.
  64.     // This extends the array by one item.
  65.     
  66.     InsertElement( index - 1 );
  67.     SetArrayItem( item, index );
  68.     
  69.     SendMessage( msgArrayItemInserted, (void*) index );
  70. }
  71.  
  72.  
  73. /*--------------------------------***  APPENDITEM  ***----------------------------------*/
  74. /*    
  75. add an item at the end of the array
  76. ----------------------------------------------------------------------------------------*/
  77.  
  78. void    ZArray::AppendItem(void* item)
  79. {
  80.     // adds the item to the end of the array. This grows it by one item.
  81.     
  82.     InsertElement( numElements );
  83.     SetArrayItem( item, numElements );
  84.     
  85.     SendMessage( msgArrayItemAdded, (void*) numElements );
  86. }
  87.  
  88.  
  89. /*-------------------------------***  SETARRAYITEM  ***---------------------------------*/
  90. /*    
  91. replace an item at position <index>
  92. ----------------------------------------------------------------------------------------*/
  93.  
  94. void    ZArray::SetArrayItem(void* item, const long index)
  95. {
  96.     // sets the item into the array at <index>, which is one-based.
  97.     
  98.     ASSERT( "Index out of range; ZArray::SetArrayItem",  index >= 0 && index <= numElements, index )        
  99.     
  100.     BlockMoveData(item, (*a + (blkSize * (index - 1))), blkSize);
  101.     
  102.     SendMessage( msgArrayItemChanged, (void*) index );
  103. }
  104.  
  105.  
  106. /*-------------------------------***  GETARRAYITEM  ***---------------------------------*/
  107. /*    
  108. return the item at position <index> (returns a COPY)
  109. ----------------------------------------------------------------------------------------*/
  110.  
  111. void    ZArray::GetArrayItem(void* item, const long index)
  112. {
  113.     // gets the item at poition <index> in the array. Index is one-based.
  114.     
  115.     ASSERT( "Index out of range; ZArray::GetArrayItem",  index >= 0 && index <= numElements, index )        
  116.     
  117.     BlockMoveData((*a + (blkSize * (index - 1))), item, blkSize);
  118. }
  119.  
  120.  
  121. /*---------------------------***  CONCATENATEARRAY  ***---------------------------------*/
  122. /*    
  123. appends the array passed to the end of this one. This does not attempt to maintain sort
  124. order, etc- it just joins the arrays. The source array is unaffected and its data is
  125. copied. NOTE: The two arrays *MUST* have the same sized elements- you cannot concatenate
  126. arrays that contain incompatible data types.
  127. ----------------------------------------------------------------------------------------*/
  128.  
  129. void    ZArray::ConcatenateArray( ZArray* anArray )
  130. {
  131.     long    appendedItems;
  132.     long    physExtent;
  133.     
  134.     FailNILParam( anArray );
  135.     
  136.     // check element sizes are compatible:
  137.     
  138.     ASSERT( "Block sizes don't match; ZArray::ConcatentateArray", anArray->blkSize == blkSize, blkSize )
  139.     
  140.     // is there anything to copy?
  141.     
  142.     if (( appendedItems = anArray->CountItems()) > 0 )
  143.     {
  144.         // extend our handle to accommodate the additional stuff. Note that since we allocate
  145.         // in multiples of a physical block count, we need to take this into account when
  146.         // joining the arrays
  147.         
  148.         physExtent = appendedItems + numElements;
  149.         physExtent += kNumPhysicalBlockAlloc - ( physExtent % kNumPhysicalBlockAlloc );
  150.         
  151.         if ( physExtent > physicalBlks )
  152.         {
  153.             physicalBlks = physExtent;
  154.             
  155.             SetHandleSize( a, physicalBlks * blkSize );
  156.             FailMemError();
  157.         }
  158.         
  159.         Ptr p = *anArray->a;
  160.         Ptr    q = *a + ( numElements * blkSize );
  161.         
  162.         BlockMoveData( p, q, appendedItems * blkSize );
  163.         
  164.         numElements += appendedItems;
  165.     }
  166. }
  167.  
  168.  
  169. /*---------------------------------***  FINDINDEX  ***----------------------------------*/
  170. /*
  171. returns the index of an item in the array, or 0 if not found.    
  172. ----------------------------------------------------------------------------------------*/
  173.  
  174. long    ZArray::FindIndex(void* item)
  175. {
  176.     // performs a linear search and returns the index of the item, if found.
  177.     
  178.     if ( numElements < 1 )
  179.         return 0;
  180.         
  181.     long        i = 0;
  182.     Boolean        found = FALSE;
  183.     
  184.     do
  185.     {
  186.         if (EqualMem(*a + ( blkSize * i ), item, blkSize))
  187.         {
  188.             found = TRUE;
  189.             break;
  190.         }
  191.     }
  192.     while( i++ < numElements );
  193.  
  194.     return ( found? i + 1 : 0 );
  195. }
  196.  
  197. /*--------------------------------***  DELETEITEM  ***----------------------------------*/
  198. /*    
  199. Delete an item at position <index>
  200. ----------------------------------------------------------------------------------------*/
  201.  
  202. void    ZArray::DeleteItem( const long index )
  203. {
  204.     // deletes the item at position <index> (1-based). Items above are moved down one.
  205.     
  206.     DeleteElement( index - 1 );
  207.     
  208.     SendMessage( msgArrayItemDeleted, (void*) index );
  209. }
  210.  
  211.  
  212. /*----------------------------------***  MOVEITEM  ***----------------------------------*/
  213. /*    
  214. move an item from one place in the array to another
  215. ----------------------------------------------------------------------------------------*/
  216.  
  217. void    ZArray::MoveItem( const long curIndex, const long newIndex )
  218. {
  219.     // moves the item at <curIndex> to position <newIndex> (all 1-based), moving other items
  220.     // as needed to keep things in order.
  221.     
  222.     ASSERT( "Bad index; ZArray::MoveItem", curIndex > 0 && curIndex <= numElements && newIndex > 0 && newIndex <= numElements, 0 )
  223.     
  224.     void* temp = (void*) NewPtr( blkSize );
  225.     
  226.     FailNIL(temp);
  227.     
  228.     // copy the item we want to move to a temporary space
  229.     
  230.     GetArrayItem( temp, curIndex);
  231.     
  232.     // delete its current position, which will move stuff as needed
  233.     
  234.     DeleteElement( curIndex - 1 );
  235.     
  236.     // insert it into the new position, which moves other stuff as needed
  237.     
  238.     InsertItem( temp, newIndex );
  239.     
  240.     // get rid of the temporary space
  241.     
  242.     DisposePtr((Ptr) temp);
  243.     SendMessage( msgArrayItemMoved, (void*) newIndex );
  244. }
  245.  
  246.  
  247. /*----------------------------------***  SWAPITEM  ***----------------------------------*/
  248. /*    
  249. Swap two items in the array
  250. ----------------------------------------------------------------------------------------*/
  251.  
  252. void    ZArray::Swap( const long itema, const long itemb )
  253. {
  254.     // swaps items a and b.
  255.     ASSERT( "Bad index; ZArray::Swap", itema > 0 && itema <= numElements && itemb > 0 && itemb <= numElements, 0 )
  256.     
  257.     // make some swap space
  258.     
  259.     void* tempa = (void*) NewPtr( blkSize );
  260.     void* tempb = (void*) NewPtr( blkSize );
  261.     FailNIL(tempa);
  262.     FailNIL(tempb);
  263.     
  264.     GetArrayItem( tempa, itema);
  265.     GetArrayItem( tempb, itemb);
  266.     SetArrayItem( tempa, itemb);
  267.     SetArrayItem( tempb, itema);
  268.     
  269.     DisposePtr((Ptr) tempa);
  270.     DisposePtr((Ptr) tempb);
  271.     
  272.     SendMessage( msgArrayItemMoved, (void*) itema );
  273. }
  274.  
  275.  
  276. /*---------------------------------***  COUNTITEMS  ***---------------------------------*/
  277. /*    
  278. return the number of items in the array
  279. ----------------------------------------------------------------------------------------*/
  280.  
  281. long    ZArray::CountItems()
  282. {
  283.     return numElements;
  284. }
  285.  
  286.  
  287. /*----------------------------------***  DOFOREACH  ***---------------------------------*/
  288. /*    
  289. for each item in the array, pass it to the grovelling proc passed
  290. ----------------------------------------------------------------------------------------*/
  291.  
  292. void    ZArray::DoForEach(IteratorProcPtr aProc, const long ref)
  293. {
  294.     if (aProc && (numElements > 0))
  295.     {
  296.         long    i = numElements;
  297.         void*    temp = (void*) NewPtr( blkSize );
  298.         
  299.         FailNIL( temp );
  300.         
  301.         while ( i )
  302.         {
  303.             GetArrayItem( temp, i);
  304.             (*aProc)(temp, ref);
  305.             
  306.             // in case the proc changed the item, set it back
  307.             
  308.             SetArrayItem( temp, i--);
  309.         }
  310.         
  311.         DisposePtr((Ptr) temp);
  312.     }
  313. }
  314.  
  315.  
  316. /*-------------------------------------***  SORT  ***-----------------------------------*/
  317. /*    
  318. sort the items in the array into order. The supplied comparison function allows you to
  319. sort anything based on any ordering criteria. Uses a very fast shellsort algorithm. Your
  320. sort function needs to examine the relevant criteria in the items passed, and decide what
  321. order they come in. It should return -1 if a < b, +1 if a > b, and 0 if equal. <ref> can be
  322. anything you want- it is simply passed to the compare function. It might be another object
  323. for example (hint, hint!).
  324.  
  325. ----------------------------------------------------------------------------------------*/
  326.  
  327. void    ZArray::Sort( register SortCmpProcPtr compareProc, register const long ref )
  328. {
  329.     register long    E,N,M,J,K,R;
  330.     register short    cp;
  331.     
  332.     register void*    itema;
  333.     register void*    itemb;
  334.     
  335.     // sanity check- there IS a sort function, right?
  336.     
  337.     FailOSErr((compareProc == NULL)? kUndefinedCompProcErr : noErr );
  338.     
  339.     // allocate some temporary storage
  340.     
  341.     FailNIL(itema = (void*) NewPtr( blkSize ));
  342.     FailNIL(itemb = (void*) NewPtr( blkSize ));
  343.     
  344.     // initialise the control variables to the number of elements in the list
  345.     
  346.     M = E = N = numElements;
  347.     N++;
  348.     
  349.     // and... sort!
  350.     
  351.     do
  352.     {
  353.         M /= 2;
  354.         if (M <= 0)
  355.             break;
  356.             
  357.         K = E - M;
  358.         J = 1;
  359.         
  360.         do
  361.         {
  362.             N = J;
  363.             do
  364.             {
  365.                 R = N + M;
  366.                 GetArrayItem( itema, N );                        // get first item
  367.                 GetArrayItem( itemb, R );                        // get second item
  368.                 
  369.                 cp = (*compareProc)( itema, itemb, ref );        // call the comparison function
  370.                 
  371.                 if ( cp < 1 )                                    // no need to swap (a <= b)
  372.                     break;
  373.                     
  374.                 Swap( N, R );                                    // swap items in the array
  375.                     
  376.                 N -= M;
  377.             }
  378.             while ( N > 0 );
  379.             J++;
  380.         }
  381.         while (J <= K);
  382.     }
  383.     while( M > 0 );
  384.     
  385.     // all done, now release the temporary storage
  386.     
  387.     DisposePtr((Ptr) itema);
  388.     DisposePtr((Ptr) itemb);
  389. }
  390.  
  391. /*-------------------------------------***  SORT  ***-----------------------------------*/
  392. /*    
  393. simpler interface to Sort- indirectly calls the Compare method, which you can override.
  394.  
  395. ----------------------------------------------------------------------------------------*/
  396.  
  397. void    ZArray::Sort()
  398. {
  399.     Sort( vCompareFunc, (long) this );
  400. }
  401.  
  402. /*----------------------------------***  COMPARE  ***-----------------------------------*/
  403. /*
  404. compare two items and return their relative ordering: -1 if a < b, +1 if a > b, 0 if a == b.
  405. You need to override this if you wish to use the simple call to Sort() above.    
  406. ----------------------------------------------------------------------------------------*/
  407.  
  408. short    ZArray::Compare( void* itema, void* itemb, const long ref )
  409. {
  410.     return 0;
  411. }
  412.  
  413. /*------------------------------***  INSERTSORTEDITEM  ***------------------------------*/
  414. /*
  415. insert the item into the list at the correct place assuming the list is sorted. This
  416. does a binary search to locate the position, based on the comparison function provided.
  417. Normally the comparison function is the same one you'd use with sort, so everything
  418. agrees. Returns the index poistion it was inserted at.
  419. ----------------------------------------------------------------------------------------*/
  420.  
  421. long    ZArray::InsertSortedItem( void* item, SortCmpProcPtr compareProc, const long ref )
  422. {
  423.     long    pos = BFindIndex( item, compareProc, ref );
  424.     
  425.     if ( pos > 0 )
  426.         InsertItem( item, pos );
  427.         
  428.     return pos;
  429. }
  430.  
  431. /*------------------------------***  INSERTSORTEDITEM  ***------------------------------*/
  432. /*
  433. insert the item into the list at the correct place assuming the list is sorted. This uses
  434. the built in compare function which calls the Compare method.
  435. ----------------------------------------------------------------------------------------*/
  436.  
  437. long    ZArray::InsertSortedItem( void* item, const long ref )
  438. {
  439.     return InsertSortedItem( item, vCompareFunc, ref );
  440. }
  441.  
  442.  
  443. /*-----------------------------***  Protected Members  ***------------------------------*/
  444.  
  445.  
  446. /*-------------------------------***  INSERTELEMENT  ***--------------------------------*/
  447. /*    
  448. make space for one element in the handle
  449. ----------------------------------------------------------------------------------------*/
  450.  
  451. void    ZArray::InsertElement( const long index )
  452. {
  453.     // grow the handle by one element, moving items above <index> up one. This also
  454.     // sets the numElements data member. Index is zero-based.
  455.     
  456.     long    newSize;
  457.     
  458.     // check that the index parameter is sensible
  459.     
  460.     ASSERT( "Index out of range; ZArray::InsertElement",  index >= 0 && index <= numElements, index )
  461.     
  462.     // grow the handle if the number of physical blocks is insufficient
  463.     
  464.     newSize = ( numElements + 1 ) * blkSize;
  465.     
  466.     if ( newSize > ( physicalBlks * blkSize ))
  467.     {
  468.         physicalBlks += kNumPhysicalBlockAlloc;
  469.         
  470.         SetHandleSize( a, physicalBlks * blkSize );
  471.         FailOSErr(MemError());
  472.     }
  473.     
  474.     // OK, the handle is now larger by one element- do we need to move any data around?
  475.     
  476.     if (index < numElements)
  477.     {
  478.         // yes, subsequent entries move up by <blkSize> bytes
  479.         
  480.         HLock( a );
  481.         BlockMoveData(    *a + (blkSize * index),
  482.                         *a + (blkSize * (index + 1)),
  483.                         blkSize * (numElements - index));
  484.         HUnlock( a );
  485.     }
  486.     
  487.     // increment the count of elements
  488.     
  489.     numElements++;
  490. }
  491.  
  492. /*-------------------------------***  DELETEELEMENT  ***--------------------------------*/
  493. /*    
  494. remove space for one element in the handle
  495. ----------------------------------------------------------------------------------------*/
  496.  
  497. void    ZArray::DeleteElement( const long index )
  498. {
  499.     // shrink the handle by one element, after moving entries above <index> down by one.
  500.     // <index> is zero-based.
  501.     
  502.     // check that the index parameter is sensible
  503.     
  504.     ASSERT( "Index out of range; ZArray::DeleteElement",  index >= 0 && index < numElements, index )        
  505.     
  506.     // one less element
  507.     
  508.     numElements--;
  509.         
  510.     // if the index is not the last item, move everything down to fill the space
  511.     
  512.     if (index < numElements)
  513.     {
  514.         HLock( a );
  515.         BlockMoveData(    *a + (blkSize * (index + 1)),
  516.                         *a + (blkSize * index),
  517.                         blkSize * (numElements - index + 1));
  518.         HUnlock( a );
  519.     }
  520.     
  521.     // shrink the handle if we can remove a whole multiple of physical blocks
  522.     
  523.     if (( physicalBlks - numElements ) > kNumPhysicalBlockAlloc )
  524.     {
  525.         physicalBlks = MAX( 0, physicalBlks - kNumPhysicalBlockAlloc );
  526.         SetHandleSize( a, blkSize * physicalBlks );
  527.     }
  528. }
  529.  
  530.  
  531. /*--------------------------------***  BINARYSEARCH  ***--------------------------------*/
  532. /*    
  533. use the compare function to determine where the item SHOULD be inserted
  534. ----------------------------------------------------------------------------------------*/
  535.  
  536. long    ZArray::BFindIndex( void* item, SortCmpProcPtr compareProc, const long ref )
  537. {
  538.     unsigned long    lowItem, highItem, midItem;
  539.     short            compare;
  540.     void*            itemB;
  541.     long            pos = 1;
  542.     
  543.     FailNILParam( compareProc );
  544.     
  545.     lowItem = 0;
  546.     highItem = numElements + 1;
  547.     
  548.     // if the list is empty, we return item 1, since we can simply append the item.
  549.     
  550.     if ( numElements < 1 )
  551.         pos = 1;
  552.     else
  553.     {
  554.         // make space for the item we are going to compare
  555.         
  556.         FailNIL( itemB = (void*) NewPtr( blkSize ));
  557.         
  558.         while (( highItem - lowItem ) > 1 )
  559.         {
  560.             midItem = ( highItem + lowItem ) >> 1;
  561.             
  562.             GetArrayItem( itemB, midItem );
  563.             
  564.             // compare this item to the one we are looking for
  565.             
  566.             compare = (*compareProc)( item, itemB, ref );
  567.             
  568.             if ( compare > 0 )
  569.                 lowItem = midItem;
  570.             else
  571.                 highItem = midItem;
  572.         }
  573.         
  574.         DisposePtr((Ptr) itemB );
  575.         pos = MAX( midItem, 1 );
  576.     }
  577.     
  578.     return pos;
  579. }
  580.  
  581.  
  582. /*------------------------------***  READFROMSTREAM  ***--------------------------------*/
  583. /*    
  584. rebuild/initialise array from stream. This overwrites/removes any existing contents.
  585. ----------------------------------------------------------------------------------------*/
  586.  
  587. void    ZArray::ReadFromStream( ZStream* aStream )
  588. {
  589. #if _MACZOOP_STREAMS
  590.     ZComrade::ReadFromStream( aStream );
  591.     
  592.     // read array data items from the stream. First item is block size, then count.
  593.     
  594.     aStream->ReadLong( &blkSize );
  595.     aStream->ReadLong( &numElements );
  596.     
  597.     // if num elements is not 0, read data into array's storage handle
  598.     
  599.     if ( numElements > 0 )
  600.     {
  601.         if ( a )
  602.             DisposeHandle( a );
  603.         
  604.         aStream->ReadHandle( &a );
  605.         
  606.         // the number of phyical blocks needs to be adjusted to this size.
  607.         // We do not bother rounding this up to a whole multiple of
  608.         // the physical block count- it'll work anyway.
  609.         
  610.         physicalBlks = GetHandleSize( a ) / blkSize;
  611.     }
  612. #endif
  613. }
  614.  
  615.  
  616. /*-------------------------------***  WRITETOSTREAM  ***--------------------------------*/
  617. /*    
  618. write entire array object to stream
  619. ----------------------------------------------------------------------------------------*/
  620.  
  621. void    ZArray::WriteToStream( ZStream* aStream )
  622. {
  623. #if _MACZOOP_STREAMS
  624.     ZComrade::WriteToStream( aStream );
  625.     
  626.     // write all the array items to the stream.
  627.     
  628.     long    i, numItems;
  629.     void*    temp;
  630.     
  631.     numItems = CountItems();
  632.     
  633.     // first two items in stream are block size and item count
  634.     
  635.     aStream->WriteLong( blkSize );
  636.     aStream->WriteLong( numItems );
  637.     
  638.     // ...followed by the data:
  639.     
  640.     if ( numItems > 0 )
  641.         aStream->WriteHandle( a );
  642. #endif
  643. }
  644.  
  645.  
  646.  
  647. #pragma mark -
  648. /*--------------------------------***  vCompareFunc  ***--------------------------------*/
  649.  
  650. static short    vCompareFunc( void* a, void* b, const long ref)
  651. {
  652.     ZArray*        theArray = (ZArray*) ref;
  653.     
  654.     if ( theArray )
  655.         return theArray->Compare( a, b, ref );    
  656.     else
  657.         return 0;
  658. }
  659.  
  660.  
  661. /*----------------------------------***  EQUALMEM  ***----------------------------------*/
  662. /*    
  663. compare two blocks of memory and return TRUE if they are equal. Zero-length data is not
  664. considered equal.
  665. ----------------------------------------------------------------------------------------*/
  666.  
  667. Boolean        EqualMem( void* a, void* b, const unsigned long length )
  668. {
  669.     Boolean        result = (length > 0);
  670.     Ptr            aa, bb;
  671.     
  672.     register unsigned long    len = length;
  673.     
  674.     aa = (Ptr) a;
  675.     bb = (Ptr) b;
  676.     
  677.     while( len-- )
  678.     {
  679.         if ( *aa++ != *bb++ )
  680.         {
  681.             result = FALSE;
  682.             break;
  683.         }
  684.     }
  685.  
  686.     return result;
  687. }
  688.  
  689.  
  690. /*--------------------------------***  EQUALHANDLE  ***---------------------------------*/
  691. /*    
  692. compare the contents of two handles and return TRUE if they're equal. If one or both
  693. handles are NULL, this returns FALSE. Handles with differing sizes are not considered
  694. equal even if the smaller handle's data matches the other handle's data exactly for its
  695. length. Internally this calls EqualMem above. If both handles are empty or zero sized,
  696. this will return FALSE, even though they are "equal" in one sense.
  697. ----------------------------------------------------------------------------------------*/
  698.  
  699. Boolean        EqualHandle( Handle a, Handle b )
  700. {
  701.     long    siza, sizb;
  702.     
  703.     if ( a == NULL || b == NULL )
  704.         return FALSE;
  705.         
  706.     siza = GetHandleSize( a );
  707.     sizb = GetHandleSize( b    );
  708.     
  709.     if ( siza != sizb )
  710.         return FALSE;
  711.         
  712.     return    EqualMem( *a, *b, siza );
  713. }
  714.  
  715.  
  716.